View Javadoc

1   /*
2    * TouchGraph LLC. Apache-Style Software License
3    *
4    *
5    * Copyright (c) 2001-2002 Alexander Shapiro. All rights reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without
8    * modification, are permitted provided that the following conditions
9    * are met:
10   *
11   * 1. Redistributions of source code must retain the above copyright
12   *    notice, this list of conditions and the following disclaimer. 
13   *
14   * 2. Redistributions in binary form must reproduce the above copyright
15   *    notice, this list of conditions and the following disclaimer in
16   *    the documentation and/or other materials provided with the
17   *    distribution.
18   *
19   * 3. The end-user documentation included with the redistribution,
20   *    if any, must include the following acknowledgment:  
21   *       "This product includes software developed by 
22   *        TouchGraph LLC (http://www.touchgraph.com/)."
23   *    Alternately, this acknowledgment may appear in the software itself,
24   *    if and wherever such third-party acknowledgments normally appear.
25   *
26   * 4. The names "TouchGraph" or "TouchGraph LLC" must not be used to endorse 
27   *    or promote products derived from this software without prior written 
28   *    permission.  For written permission, please contact 
29   *    alex@touchgraph.com
30   *
31   * 5. Products derived from this software may not be called "TouchGraph",
32   *    nor may "TouchGraph" appear in their name, without prior written
33   *    permission of alex@touchgraph.com.
34   *
35   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38   * DISCLAIMED.  IN NO EVENT SHALL TOUCHGRAPH OR ITS CONTRIBUTORS BE 
39   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
40   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
41   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
42   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
43   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
44   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
45   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46   * ====================================================================
47   *
48   */
49  
50  package com.touchgraph.graphlayout;
51  
52  import java.awt.*;
53  import java.util.*;
54  
55  /***  Node.
56    *
57    *  @author   Alexander Shapiro
58    *  @author   Murray Altheim (2001-11-06; added support for round rects and alternate Node colors)
59    *  @version  1.21  $Id: Node.java,v 1.1.1.1 2004/02/06 08:44:05 keesj Exp $
60    */
61  public class Node {
62      
63     /*** This Node's type is a Rectangle. */
64      public final static int TYPE_RECTANGLE = 1;
65  
66     /*** This Node's type is a Round Rectangle. */
67      public final static int TYPE_ROUNDRECT = 2;
68  
69     /*** This Node's type is an Ellipse. */
70      public final static int TYPE_ELLIPSE   = 3;
71  
72     /*** This Node's type is a Circle. */
73      public final static int TYPE_CIRCLE    = 4;
74  
75      public static final Font SMALL_TAG_FONT = new Font("Courier",Font.PLAIN,9);
76  
77     // Variables that store default values for colors + fonts + node type
78      public static Color BACK_FIXED_COLOR        = Color.red;
79      public static Color BACK_SELECT_COLOR       = new Color(255, 224, 0);
80      public static Color BACK_DEFAULT_COLOR      = new Color(208, 96, 0);
81      public static Color BACK_HILIGHT_COLOR      = Color.decode("#ffb200"); // altheim: new
82      
83      public static Color BORDER_DRAG_COLOR       = Color.black;
84      public static Color BORDER_MOUSE_OVER_COLOR = new Color(160,160,160);
85      public static Color BORDER_INACTIVE_COLOR   = Color.white;
86  
87      public static Color TEXT_COLOR              = Color.white;
88      
89      public static Font TEXT_FONT = new Font("Courier",Font.PLAIN,12);
90      
91      public static int DEFAULT_TYPE = 1;
92      
93     /*** an int indicating the Node type. 
94       * @see TYPE_RECTANGLE
95       * @see TYPE_ROUNDRECT
96       * @see TYPE_ELLIPSE
97       */
98      protected int typ = TYPE_RECTANGLE;
99      private String id;
100 
101     public double drawx;
102     public double drawy;
103 
104     protected FontMetrics fontMetrics;
105     protected Font font;
106 
107     protected String lbl;
108     protected Color backColor = BACK_DEFAULT_COLOR;
109     protected Color textColor = TEXT_COLOR;
110         
111     public double x;
112     public double y;
113 
114     protected double dx; //Used by layout
115     protected double dy; //Used by layout
116 
117     protected boolean fixed;
118     protected int repulsion; //Used by layout
119 
120     public boolean justMadeLocal = false;
121     public boolean markedForRemoval = false;
122     
123     public int visibleEdgeCnt; //Should only be modified by graphelements.VisibleLocality
124     protected boolean visible;
125 
126     private Vector edges;
127 
128 
129   // ............
130 
131    /*** Minimal constructor which will generate an ID value from Java's Date class.
132      * Defaults will be used for type and color. The label will be taken from the ID value.
133      */
134     public Node()
135     {
136         initialize(null);
137         lbl = id;
138     }
139 
140    /*** Constructor with the required ID <tt>id</tt>, using defaults 
141      * for type (rectangle), color (a static variable from TGPanel).
142      * The Node's label will be taken from the ID value.
143      */
144     public Node( String id )
145     {
146         initialize(id);
147         lbl = id;
148     }
149 
150    /*** Constructor with Strings for ID <tt>id</tt> and <tt>label</tt>, using defaults 
151      * for type (rectangle) and color (a static variable from TGPanel).
152      * If the label is null, it will be taken from the ID value.
153      */
154     public Node( String id, String label )
155     {
156         initialize(id);
157         if ( label == null ) lbl = id;
158         else lbl = label;
159     }
160 
161    /*** Constructor with a String ID <tt>id</tt>, an int <tt>type</tt>, Background Color <tt>bgColor</tt>, 
162      * and a String <tt>label</tt>. If the label is null, it will be taken from the ID value.
163      * @see TYPE_RECTANGLE
164      * @see TYPE_ROUNDRECT
165      */
166     public Node( String id, int type, Color color, String label )
167     {
168         initialize(id);
169         typ = type;
170         backColor = color;
171         if ( label == null ) lbl = id;
172         else lbl = label;
173     }
174 
175     private void initialize( String identifier ) {
176         this.id = identifier;
177         edges = new Vector();
178         x = Math.random()*2-1; // If multiple nodes are added without repositioning,
179         y = Math.random()*2-1; // randomizing starting location causes them to spread out nicely.
180         repulsion = 100;
181         font = TEXT_FONT;
182         fixed = false;
183         typ = DEFAULT_TYPE;
184         visibleEdgeCnt=0;
185         visible = false;
186     }
187 
188 
189    // setters and getters ...............
190    
191     public static void setNodeBackFixedColor( Color color ) { BACK_FIXED_COLOR = color; }
192     public static void setNodeBackSelectColor( Color color ) { BACK_SELECT_COLOR = color; }
193     public static void setNodeBackDefaultColor( Color color ) { BACK_DEFAULT_COLOR = color; }
194     public static void setNodeBackHilightColor( Color color ) { BACK_HILIGHT_COLOR = color; }
195     public static void setNodeBorderDragColor( Color color ) { BORDER_DRAG_COLOR = color; }
196     public static void setNodeBorderMouseOverColor( Color color ) { BORDER_MOUSE_OVER_COLOR = color; }
197     public static void setNodeBorderInactiveColor( Color color ) { BORDER_INACTIVE_COLOR = color; }
198     public static void setNodeTextColor( Color color ) { TEXT_COLOR = color; }
199     public static void setNodeTextFont( Font font ) { TEXT_FONT = font; }
200     public static void setNodeType( int type ) { DEFAULT_TYPE = type; }
201   
202     /*** Set the ID of this Node to the String <tt>id</tt>.
203       */
204     public void setID( String id ) {
205         this.id = id;
206     }
207 
208     /*** Return the ID of this Node as a String.
209      */
210     public String getID() {
211         return id;
212     }
213 
214     /*** Set the location of this Node provided the Point <tt>p</tt>.
215       */
216     public void setLocation( Point p ) {
217         this.x = p.x;
218         this.y = p.y;
219     }
220  
221  
222     /*** Return the location of this Node as a Point.
223       */
224     public Point getLocation() {
225         return new Point((int)x,(int)y);
226     }
227 
228     /*** Set the visibility of this Node to the boolean <tt>v</tt>. 
229       */
230     public void setVisible( boolean v) {
231         visible = v;
232     } 
233 
234     /*** Return the visibility of this Node as a boolean.
235       */
236     public boolean isVisible() {
237         return visible;
238     } 
239  
240     /*** Set the type of this Node to the int <tt>type</tt>.
241       * @see TYPE_RECTANGLE
242       * @see TYPE_ROUNDRECT 
243       * @see TYPE_ELLIPSE
244       * @see TYPE_CIRCLE
245       */
246       
247     public void setType( int type ) {
248         typ = type;
249     }
250 
251     /*** Return the type of this Node as an int.
252       * @see TYPE_RECTANGLE
253       * @see TYPE_ROUNDRECT 
254       * @see TYPE_ELLIPSE
255       * @see TYPE_CIRCLE
256       */
257     public int getType() {
258         return typ;
259     }
260  
261     /*** Set the font of this Node to the Font <tt>font</tt>. */
262     public void setFont( Font font ) {
263         this.font = font;
264     }
265 
266     /*** Returns the font of this Node as a Font*/
267     public Font getFont() {
268         return font;
269     }
270   
271     /*** Set the background color of this Node to the Color <tt>bgColor</tt>. */
272     public void setBackColor( Color bgColor ) {
273         backColor = bgColor;
274     }
275 
276    /*** Return the background color of this Node as a Color.
277      */
278     public Color getBackColor() {
279         return backColor;
280     }
281 
282     /*** Set the text color of this Node to the Color <tt>txtColor</tt>. */
283     public void setTextColor( Color txtColor ) {
284         textColor = txtColor;
285     }
286    
287     
288    /*** Return the text color of this Node as a Color.
289      */
290     public Color getTextColor() {
291         return textColor;
292     }
293 
294 
295    /*** Set the label of this Node to the String <tt>label</tt>. */
296     public void setLabel( String label ) {
297         lbl = label;
298     }
299 
300    /*** Return the label of this Node as a String.
301      */
302     public String getLabel() {
303         return lbl;
304     }
305 
306    /*** Set the fixed status of this Node to the boolean <tt>fixed</tt>. */
307     public void setFixed( boolean fixed ) {
308         this.fixed = fixed;
309     }
310  
311  
312    /*** Returns true if this Node is fixed (in place).
313      */
314     public boolean getFixed() {
315         return fixed;
316     }
317  
318     // ....
319 
320     /*** Return the number of Edges in the cumulative Vector. 
321       * @deprecated        this method has been replaced by the <tt>edgeCount()</tt> method.
322       */
323     public int edgeNum() {
324         return edges.size(); 
325     }
326 
327     /*** Return the number of Edges in the cumulative Vector. */
328     public int edgeCount() {
329         return edges.size(); 
330     }
331 
332     /*** Return an iterator over the Edges in the cumulative Vector, null if it is empty. */
333     public Iterator getEdges() {
334         if ( edges.size() == 0 ) return null;
335         else return edges.iterator(); 
336     }
337 
338     /*** Returns the local Edge count. */
339     public int visibleEdgeCount() {
340         return visibleEdgeCnt;
341     }
342 
343     /*** Return the Edge at int <tt>index</tt>. */
344     public Edge edgeAt( int index ) {
345         return (Edge)edges.elementAt(index);
346     }
347 
348     /*** Add the Edge <tt>edge</tt> to the graph. */
349     public void addEdge( Edge edge ) {
350         if ( edge == null ) return;
351         edges.addElement(edge);
352     }
353 
354     /*** Remove the Edge <tt>edge</tt> from the graph. */
355     public void removeEdge( Edge edge ) {
356         edges.removeElement(edge);
357     }
358 
359     /*** Return the width of this Node. */
360     public int getWidth() {
361         if ( fontMetrics != null && lbl != null ) {
362             return fontMetrics.stringWidth(lbl) + 12;            
363         } else {
364             return 10;
365         }
366     }
367 
368     /*** Return the height of this Node. */
369     public int getHeight() {
370         if ( fontMetrics != null ) {
371             return fontMetrics.getHeight() + 6;
372         } else {
373             return 6;
374         }
375     }
376 
377     /*** Returns true if this Node intersects Dimension <tt>d</tt>. */
378     public boolean intersects( Dimension d ) {
379         return ( drawx > 0 && drawx < d.width && drawy>0 && drawy < d.height );
380     }
381 
382     /*** Returns true if this Node contains the Point <tt>px,py</tt>. */
383     public boolean containsPoint( double px, double py ) {
384         return (( px > drawx-getWidth()/2) && ( px < drawx+getWidth()/2) 
385                 && ( py > drawy-getHeight()/2) && ( py < drawy+getHeight()/2));
386     }
387 
388     /*** Returns true if this Node contains the Point <tt>p</tt>. */
389     public boolean containsPoint( Point p ) {
390         return (( p.x > drawx-getWidth()/2) && ( p.x < drawx+getWidth()/2) 
391                 && ( p.y > drawy-getHeight()/2) && ( p.y < drawy+getHeight()/2));
392     }
393 
394     /*** Paints the Node. */
395     public void paint( Graphics g, TGPanel tgPanel ) {
396         if (!intersects(tgPanel.getSize()) ) return;
397         paintNodeBody(g, tgPanel);
398             
399         if ( visibleEdgeCount()<edgeCount() ) {
400             int ix = (int)drawx;
401             int iy = (int)drawy;
402             int h = getHeight();
403             int w = getWidth();
404             int tagX = ix+(w-7)/2-2+w%2;
405             int tagY = iy-h/2-2;
406             char character;
407             int hiddenEdgeCount = edgeCount()-visibleEdgeCount();
408             character = (hiddenEdgeCount<9) ? (char) ('0' + hiddenEdgeCount) : '*';
409             paintSmallTag(g, tgPanel, tagX, tagY, Color.red, Color.white, character);
410         }
411     }
412 
413     public Color getPaintBorderColor(TGPanel tgPanel) {
414         if (this == tgPanel.getDragNode()) return BORDER_DRAG_COLOR;
415         else if (this == tgPanel.getMouseOverN()) return BORDER_MOUSE_OVER_COLOR;
416         else return BORDER_INACTIVE_COLOR;
417     }
418 
419     public Color getPaintBackColor(TGPanel tgPanel) {
420         if ( this == tgPanel.getSelect() ) {
421             return BACK_SELECT_COLOR;
422         } else {
423             if (fixed) return BACK_FIXED_COLOR;
424             if (markedForRemoval) return new Color(100,60,40);
425             if (justMadeLocal) return new Color(255,220,200);
426             return backColor;            
427         }
428     }        
429     
430     public Color getPaintTextColor(TGPanel tgPanel) {
431         return textColor;
432     }
433 
434     /*** Paints the background of the node, along with its label */
435     public void paintNodeBody( Graphics g, TGPanel tgPanel) {
436         g.setFont(font);
437         fontMetrics = g.getFontMetrics();
438         
439         int ix = (int)drawx;
440         int iy = (int)drawy;
441         int h = getHeight();
442         int w = getWidth();
443         int r = h/2+1; // arc radius
444 
445         Color borderCol = getPaintBorderColor(tgPanel);
446         g.setColor(borderCol);
447 
448         if ( typ == TYPE_ROUNDRECT ) {             
449             g.fillRoundRect(ix - w/2, iy - h / 2, w, h, r, r);
450         } else if ( typ == TYPE_ELLIPSE ) {
451             g.fillOval(ix - w/2, iy - h / 2, w, h );
452         } else if ( typ == TYPE_CIRCLE ) { // just use width for both dimensions
453             g.fillOval(ix - w/2, iy - w / 2, w, w );
454         } else { // TYPE_RECTANGLE
455             g.fillRect(ix - w/2, iy - h / 2, w, h);
456         }
457 
458         Color backCol = getPaintBackColor(tgPanel);
459         g.setColor(backCol);
460 
461         if ( typ == TYPE_ROUNDRECT ) {
462             g.fillRoundRect(ix - w/2+2, iy - h / 2+2, w-4, h-4, r, r );
463         } else if ( typ == TYPE_ELLIPSE ) {
464             g.fillOval(ix - w/2+2, iy - h / 2+2, w-4, h-4 );
465         } else if ( typ == TYPE_CIRCLE ) {
466             g.fillOval(ix - w/2+2, iy - w / 2+2, w-4, w-4 );
467         } else { // TYPE_RECTANGLE
468             g.fillRect(ix - w/2+2, iy - h / 2+2, w-4, h-4);
469         }
470 
471         Color textCol = getPaintTextColor(tgPanel);
472         g.setColor(textCol);
473         g.drawString(lbl, ix - fontMetrics.stringWidth(lbl)/2, iy + fontMetrics.getDescent() +1);
474     }
475 
476     /*** Paints a tag with containing a character in a small font. */
477     public void paintSmallTag(Graphics g, TGPanel tgPanel, int tagX, int tagY, 
478                               Color backCol, Color textCol, char character) {
479         g.setColor(backCol);
480         g.fillRect(tagX, tagY, 8, 8);
481         g.setColor(textCol);
482         g.setFont(SMALL_TAG_FONT);
483         g.drawString(""+character, tagX+2, tagY+7);
484     }
485 
486 } // end com.touchgraph.graphlayout.Node